﻿#include <array>
#include <QtConcurrent/QtConcurrent>
#include <QJsonObject>
#include <QJsonArray>
#include <QDataStream>
#include <QModbusDataUnit>
#include <QModbusClient>
#include <QEventLoop>
//#include "ControlBridge.h"
#include "../Shared/ModbusClient.h"
#include "./Frameworks/AbstruactEventMediator.h"
#include "./Frameworks/MediatorEventDefine.h"
#include "ModbusClientProxy.h"

//由中介者发出
void ModbusClientProxy::update(QJsonObject const& msg) {
	QMetaObject::invokeMethod(this, [msg, this] {
		auto it = m_msgMap.find(msg[ME_MSG_KEY].toString());
		if (it != std::cend(m_msgMap)) {
			std::invoke(it->second, std::cref(msg));
		}
	});
}

//更新从接收器上来的数据
bool ModbusClientProxy::update(QModbusClient* client, QModbusDataUnit const& msg, int device) {
	QModbusDataUnit readData(QModbusDataUnit::HoldingRegisters, 0, 125);
	QVector<quint16> result;
	QEventLoop loop;
	result.reserve(125);
	QModbusReply* reply = nullptr;	
	reply = client->sendReadWriteRequest(readData, msg, device);	
	if (reply) {
		connect(reply, &QModbusReply::finished, &loop, &QEventLoop::quit);
	}
	else {
		return false;
	}
	loop.exec();

	if (reply->error() != QModbusDevice::NoError) {
		qDebug() << "error: " << reply->errorString();
		reply->deleteLater();
		return false;
	}
	else {
		result = reply->result().values().toVector();
	}

	reply->deleteLater();
	processRegister(reply->result().startAddress(), result);
	return true;
}

bool ModbusClientProxy::write(QModbusClient* client, QModbusDataUnit const& msg, int device) {
	if (!client || !msg.isValid()) {
		return false;
	}
	auto reply = client->sendWriteRequest(msg, device);
	QSharedPointer<QEventLoop> loop = QSharedPointer<QEventLoop>(new QEventLoop());
	if (!reply) {
		return false;
	}
	if (!reply->isFinished()) {
		connect(reply, &QModbusReply::finished, [loop] {
			if (loop) {
				loop->quit();
			}
		});
		loop->exec();
	}
	if (reply->error() != QModbusDevice::NoError) {
		qDebug() << reply->errorString();
		return false;
	}
	if (reply) {
		reply->deleteLater();
	}
	return true;
}

bool ModbusClientProxy::read(QModbusClient* client, int device) {
	QList<QModbusDataUnit> readDatas{ QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 0, 125),
		QModbusDataUnit(QModbusDataUnit::HoldingRegisters, 125, 50) };	
	QEventLoop loop;
	QList<QModbusReply*> replys;
	int finishedCount = 0;
	auto onFinished = [&]()->void {
		finishedCount++;
		if (finishedCount == replys.size() && loop.isRunning()) {
			loop.quit();
		}
	};
	std::for_each(std::cbegin(readDatas), std::cend(readDatas), [&](QModbusDataUnit const& dat) {
		auto reply = client->sendReadRequest(dat, device);
		if (reply) {
			connect(reply, &QModbusReply::finished, this, onFinished);
			replys << reply;
		}
	});
	if (replys.size() != readDatas.size()) {
		goto error;
	}
	loop.exec();
	std::for_each(std::cbegin(replys), std::cend(replys), [&](auto& reply) {
		if (reply->error() == QModbusDevice::NoError) {		
			processRegister(reply->result().startAddress(), reply->result().values().toVector());
		}
	});
	return true;
error:
	std::for_each(std::cbegin(replys), std::cend(replys), [](auto&& replay) {
		if (replay) {
			replay->deleteLater();
		}
	});
	return false;
}

void ModbusClientProxy::onConnected() {
	m_mediator->reportLog(QStringLiteral("成功连接接收器"));
	m_mediator->connectStateChanged(QModbusDevice::ConnectedState);
}

void ModbusClientProxy::onDisconnected() {
	m_mediator->reportLog(QStringLiteral("接收器链接断开"), 2);
	m_mediator->connectStateChanged(QModbusDevice::UnconnectedState);
	fillRegisterZero();
}

void ModbusClientProxy::onConnectClosed() {
	m_mediator->reportLog(QStringLiteral("接收器链接关闭"));
	m_mediator->connectStateChanged(QModbusDevice::ClosingState);
	fillRegisterZero();
}

void ModbusClientProxy::onSuspectNetwork(int level) {
	if (level == 1) {
		m_mediator->reportLog(QStringLiteral("网络信号较弱"), 1);
		fillRegisterZero();
	}
	else if (level >= 2) {
		m_mediator->reportLog(QStringLiteral("网络异常，已经断开，重启上位机重试！"), 1);
		QJsonObject root;
		root[ME_MSG_KEY] = ME_NETWORK_ERROR.toString();
		root["msg"] = QStringLiteral("网络错误次数超过") + QString::number(level) + QStringLiteral("次，请重新调整网络后使用！");
		send(root);
		fillRegisterZero();
	}
}

void ModbusClientProxy::onReportLogs(QString const& logs, int lev) {
	m_mediator->reportLog(QStringLiteral("上位机->接收器:") + logs, lev);
}

void ModbusClientProxy::transmit(QJsonObject const& msg) {
	auto&& startAddr = msg["startAddress"].toInt();
	auto&& jsValues = msg["values"].toArray();
	QVector<quint16> values;
	values.reserve(jsValues.size());
	for (auto it = std::cbegin(jsValues); it != std::cend(jsValues); it++) {
		values.append((*it).toInt());
	}
	QByteArray bytes;
	QDataStream in(&bytes, QIODevice::WriteOnly);
	in << values << startAddr;
	m_modbusCli->onMessage(bytes);
}


void ModbusClientProxy::processRegister(int startAddress, QVector<quint16> values) {
	QJsonObject root;
	QJsonObject valuesObj;
	int startAddr = startAddress;
	for (auto const& value : values) {
		valuesObj.insert(QString::number(startAddr), value);
		startAddr++;
	}
	root["type"] = QJsonValue();
	root[ME_MSG_KEY] = ME_UPDATEREGISTER.toString();
	root["values"] = valuesObj;
	send(root);
}

void ModbusClientProxy::fillRegisterZero() {
	QVector<quint16> values(315, 0);
	processRegister(0, values);
}

void ModbusClientProxy::send(QJsonObject const& msg) {
	if (m_mediator)
		m_mediator->push(msg, ModbusClientProxy::Identity);
}

void ModbusClientProxy::send(QJsonObject const& msg, QStringView to) {
	if (m_mediator) {
		m_mediator->push(msg, ModbusClientProxy::Identity, to);
	}
}

void ModbusClientProxy::setLocation(WorkLocation location) {
	m_location = location;
}

void ModbusClientProxy::setMediator(AbstruactEventMediator* mediator) {
	m_mediator = mediator;
}

QStringView ModbusClientProxy::identity() const {
	return ModbusClientProxy::Identity;
}

bool ModbusClientProxy::connectDevice(QJsonObject const& obj) {
	bool result = false;
	QMetaObject::invokeMethod(this, [obj = std::move(obj), this]()->bool {
		auto&& device = obj["serverId"].toInt();
		auto&& host = obj["host"].toString();
		auto&& port = obj["port"].toInt();
		auto&& timeoutInv = obj["timeout"].toInt();
		m_modbusCli->setTimeout(timeoutInv);
		m_modbusCli->setConnect(host, port);
		m_modbusCli->setDevice(device);
		return m_modbusCli->connectToServer();
	}, Qt::BlockingQueuedConnection, &result);
	return result;
}

QVariant ModbusClientProxy::request(QJsonObject const& obj, QStringView const requester) {
	QVariant result;
	(void)requester;
	QMetaObject::invokeMethod(this, [obj, this]()->QVariant {
		if (obj[ME_MSG_KEY].toString() == ME_REQUEST_CONNECT_PARAM) {
			return QVariant::fromValue(m_location);
		}
		else {
			return QVariant();
		}
	}, Qt::BlockingQueuedConnection, &result);
	return result;
}

ModbusClientProxy::ModbusClientProxy(QObject* parent)
	: QObject(parent) {
	QMetaObject::invokeMethod(this, [this] {
		m_modbusCli = new ModbusClient(this, this);
		connect(m_modbusCli, &ModbusClient::connected, this, &ModbusClientProxy::onConnected);
		connect(m_modbusCli, &ModbusClient::disconnected, this, &ModbusClientProxy::onDisconnected);
		connect(m_modbusCli, &ModbusClient::connectClosed, this, &ModbusClientProxy::onConnectClosed);
		connect(m_modbusCli, &ModbusClient::suspectNetwork, this, &ModbusClientProxy::onSuspectNetwork);
		connect(m_modbusCli, &ModbusClient::reportLogs, this, &ModbusClientProxy::onReportLogs);
	}, Qt::QueuedConnection);
	m_msgMap[ME_TRANSMIT] = std::bind(&ModbusClientProxy::transmit, this, std::placeholders::_1);
}

ModbusClientProxy::~ModbusClientProxy() {
	m_mediator->unRegisterAction(ModbusClientProxy::Identity);
	m_mediator = nullptr;
}
